perm filename ALREC.SAI[AL,HE]7 blob
sn#367349 filedate 1978-07-13 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00006 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 header files & the like
C00004 00003 ! Standard records
C00020 00004 ! data types
C00022 00005 ! operators for expressions
C00027 00006 ! predeclared variables & internal system variables
C00031 ENDMK
C⊗;
COMMENT header files & the like;
IFCR ¬DECLARATION(π) THENC
ENTRY;
BEGIN "ALREC"
REQUIRE "ABBREV.SAI[AL,HE]" SOURCE_FILE;
REQUIRE "MACROS.SAI[AL,HE]" SOURCE_FILE;
REQUIRE "RECAUX.HDR[AL,HE]" SOURCE_FILE;
REQUIRE "LEPAUX.HDR[AL,HE]" SOURCE_FILE;
REQUIRE "PRCAUX.HDR[AL,HE]" SOURCE_FILE;
REQUIRE "REFBTS.DEF[AL,HE]" SOURCE_FILE;
DEFINE ALRECTERNAL=[INTERNAL];
DEFINE ALRECEND=[END];
DEFINE ALRECSW = 1;
DEFINE NOARITH = "TRUE";
REQUIRE "ARITH.HDR[AL,HE]" SOURCE_FILE;
ELSEC
REQUIRE "[][]" DELIMITERS;
REQUIRE "ALREC.REL[AL,HE]" LOAD_MODULE;
DEFINE ALRECEND=[ ];
DEFINE ALRECTERNAL=[EXTERNAL];
DEFINE ALRECSW = 0;
ENDC
! a few constants;
DEFINE INCHES = "2.54";
DEFINE DEGREES = "(π/180.0)";
ALRECTERNAL OWN INTEGER ARRAY PATCH[0:50];
! Standard records ;
DEFINE ALRTYPE(FOO,BAR) "<>" =
< ASSIGNC BAR = CVPS(FOO)&"TYPE";
REDEFINE NHRECS=NHRECS+1;
DEFINE BAR = NHRECS>;
DEFINE ALRCLASS(FOO) "<>" =
< ALRTYPE(FOO);
ALRECTERNAL RECORD_CLASS FOO >;
DEFINE NHRECS=0;
IFCR ¬DECLARATION(_FACT_) THENC
EXTERNAL RECORD_CLASS FACT(integer fake);
ENDC
DEFINE RCELL = [RPTR(CELL)];
DEFINE REXPR = [RANY];
DEFINE RVAR = [RPTR(VARIABLE,EXPRN)];
DEFINE RSTMNT = [RPTR(STMNT)];
DEFINE RSSS = [RANY];
! Constants. **** Now, record classes defined by ARITH ****;
ALRCLASS (STCONST) (STRING ITEMVAR VAL);
ALRTYPE(V3ECT);
ALRTYPE(ROTN);
ALRTYPE(TRANS);
ALRTYPE(SVAL);
ALRTYPE(TINFO);
ALRTYPE(FRAME);
! a special sort of locus expression;
ALRCLASS (BPARAM) (REAL MXV,MNV,NV;INTEGER ROLE;RANY SEMANTICS);
! expressions;
ALRCLASS (EXPRN) (INTEGER DATATYPE,OP;RCELL ARGS);
! Variables & their attributes;
ALRCLASS (FLUENT) (RPTR(FACT) FACTID;RANY FREC,RETRPATT);
ALRCLASS (SET_FLUENT) (RANY RETRPATT);
ALRCLASS (VNODE) (INTEGER INVMARK;RPTR(VALU$) NOMVAL);
ALRCLASS (LBLVAR) (RANY ITEMVAR NAME;
INTEGER DATATYPE;RANY BLK;RANY SEMANTICS);
ALRCLASS (STMNT)(RANY ITEMVAR ID;ITEMVAR IW,OW,PRC;RSSS SEMANTICS;
RPTR(LBLVAR) STLAB);
ALRCLASS (VARIABLE)(RANY ITEMVAR NAME;INTEGER DATATYPE,OFFSET,ATTRIBUTES;
RANY SEMANTICS, BLK;
RPTR(FLUENT) PLNVAL;
RPTR(SET_FLUENT) CALCS,DEPS,CHANGERS;
! Temp for RF:; RANY VAL);
! Variable attribute bits follow: ;
BITDEF(GLBAL,'1); ! This variable is declared "global";
BITDEF(REFARG,'2); ! This variable is accessed by "reference";
BITDEF(VALARG,'4); ! This variable is accessed by "value";
! BLK is block in which this variable appears;
ALRCLASS (ARRAYDEF)(RANY ITEMVAR NAME; INTEGER DATATYPE, OFFSET,
ATTRIBUTES, NUMDIMS;
RANY SEMANTICS, BLK;
REXPR ARRAY BOUNDS; INTEGER ARRAY BDVALS;
RVAR ARRAY VARS);
ALRCLASS (PROCDEF) (RANY ITEMVAR NAME; INTEGER DATATYPE, OFFSET,
ATTRIBUTES, LAB, NUMARGS;
RCELL ARGS; RSTMNT BODY);
! Control structures;
ALRCLASS (PROG) (RPTR(STMNT) CODE);
ALRCLASS (BLOCK) (RCELL CODE,VARS,ARAYS,CMONS,EVTS,PROCS,CLCS,ALSOS;
RANY ITEMVAR BLID;
RANY PARENT);
! As used by GOBBLE, BLID⊗ident≡var;
! CLCS,CMONS,ALSOS are all lists of the corresponding
constructs.
I am assuming (for now) that all CALCs or CHANGERs
are "set up" whenever their block is entered.
This is not strictly necessary & the work may
just as well be done when needed.
It is assumed that ALSOS & CALCS will all be
undone when the block is exited. In the case of
ALSOS, this means unlinking each changer from
whatever graph nodes it is linked to & freeing
up the storage. In the case of CALCS, just
drop the pointer & allow variable killing to
do all the work. I am assuming that a changer
will be put onto the corrrect block to govern its
"life".
;
ALRCLASS (BLKOP) (INTEGER OP);
! for the moment, this construct is used
just by WLDMOD: The first statement in a block
(with declarations, etc) is BLKOP(1)
& the last statement is BLKOP(2). Perhaps
Ray would like to use this, too;
DEFINE ENTERBLOCK = 1;
DEFINE LEAVEBLOCK = 2;
ALRCLASS (CALCULATOR)(RPTR(FLUENT) PLNVAL;
RPTR(SET_FLUENT) DEPS;
SET NEEDED;REXPR FORM;INTEGER OFFSET;
RPTR(LBLVAR) LBL);
ALRCLASS (CHANGER) (INTEGER OFFSET;RPTR(BLOCK) BLID;
RPTR(SET_FLUENT) TRIGGERS;
RPTR(STMNT) CODE;RPTR(LBLVAR) LBL);
! Triggers is set of variables whose change triggers
this fellow;
ALRCLASS (COBLOCK) (RCELL CODE);
ALRCLASS (TASKBLOCK) (RCELL CODE);
ALRCLASS (FORR) (RVAR CONVAR; REXPR INITIAL, STEP, FINAL; RSTMNT BODY);
ALRCLASS (WHIL) (REXPR COND; RSTMNT BODY);
ALRCLASS (UNTL) (REXPR COND; RSTMNT BODY);
ALRCLASS (KASE) (REXPR INDEX; INTEGER RANGE, NSTMNTS;
INTEGER ARRAY LABS; RCELL STMNTS);
ALRCLASS (IFF) (REXPR COND; RSTMNT THN, ELS);
ALRCLASS (PAUSE) (REXPR VAL);
ALRCLASS (PROMPT) (REXPR VAL);
ALRCLASS (ABORT) (REXPR VAL);
ALRCLASS (RETRN) (REXPR VAL);
! graph structure manipulators;
ALRCLASS (ASSIGNMENT) (RVAR VAR; REXPR VAL);
ALRCLASS (PRNT) (REXPR VAL);
ALRCLASS (AFFIX) (RVAR FRAME1,FRAME2,BYVAR;REXPR ATEXP;RVAR RIGID;
RCELL GPHCODE);
ALRCLASS (UNFIX) (RVAR FRAME1,FRAME2;RCELL GPHCODE);
! in both these cases, GPHCODE is list of graph
assignments & alsodo's. Note: NOT of
STMNTS.
;
ALRCLASS (GASSIGN)(RPTR(VARIABLE) VAR;
INTEGER OP;RPTR(CALCULATOR,LBLVAR) CLC);
! The OP has these meanings:
1: = is computed by, "<="
2: ≠ is not computed by, "<≠"
3: < or ← is only computed by, "<<="
;
ALRCLASS (ALSODO)(RPTR(VARIABLE) VAR;INTEGER OP;RPTR(CHANGER,LBLVAR) CHG);
! ***** CHG used to be a stmnt ****;
! The OP has these meanings:
1: When changing VAR also do CODE
2: When changing VAR dont do CODE
3: When changing VAR only do CODE
;
ALRCLASS (SPECVAL)(BOOLEAN OLD; INTEGER TYPE);
! Either OLD or NEW, can be of any type;
ALRCLASS (EVDO)(RVAR VAR; INTEGER OP);
! OP = 0 for signal, =1 for wait;
ALRCLASS (DEXPR)(RVAR VAR;REXPR EXPN;RPTR(VALU$) VAL;RVAR TMPVAR);
! This construct is intended as a special kluge to
pass destination expressions on to MOVE statements
& (perhaps) other PASS3 things that need planning
values. VAR is assumed to be a variable whose
value is to be fetched at runtime. If EXPN is
not null, then VAR is probably a temp. In any event,
it is assumed that PASS3 will emit code to compute
EXPN & assign it into VAR before using. VAL
is set up by the simulator & contains the planning
value of VAR. TMPVAR is the same as VAR if VAR is
a temp. The reason for this is that the simulator may
make several passes over the same statement & I
need a way to avoid creating duplicate temps all over
the place.
;
! quasi-statements;
ALRCLASS (PVL)(RCELL VL;ITEMVAR WLD);
ALRCLASS (DBD)(ITEMVAR WLD);
ALRCLASS (NW)(ITEMVAR WLD);
! misc statements;
ALRCLASS (MOVE$) (RVAR WHAT;REXPR DEST;RCELL CLAUSES;RVAR CF;
RPTR(DEXPR) DEXP);
ALRCLASS (OPERATE) (RVAR WHAT;REXPR DEST;RCELL CLAUSES;RVAR CF;
RPTR(DEXPR) DEXP);
! CF is the controllable frame for this motion statement.
DEXP is the DEXPR that gives the ACTUAL destination for
CF.
**** At present, the program graph is NOT modified
to generate an assignment statement into DFTEMP.
It is assumed that the code generator will
generate code to evaluate the expression &
then poke the value away. WLDMOD will, however,
call CHANGE to give DFTEMP a correct planning value.
;
ALRCLASS (CENTER) (RVAR CF;RCELL CLAUSES);
ALRCLASS (STOP) (RVAR CF);
ALRCLASS (CMON) (REXPR CONDITION; RSTMNT CONCLUSION;
INTEGER OFFSET,FLAGS);
BITDEF(DEFER,'1); ! set if deferred cmon;
BITDEF(W_ARM,'2); ! set if yellow arm;
ALRCLASS (CMABLE) (RPTR(CMON,LBLVAR) WHAT;INTEGER FLAG);
! (FLAG = TRUE) => enable, else disable;
ALRCLASS (DURATION) (REXPR TIME; INTEGER TIME_RELN);
! Note: the time relations are the following:
0 no relation given
1 > (that is, a lower bound)
2 < (that is, an upper bound)
3 = (that is, an exact bound)
;
ALRCLASS (VIA) (REXPR PLACE,VELOC; RPTR(DURATION) TIME;
RSTMNT CODE;RPTR(DEXPR) ACTPLACE);
! ACTPLACE gives where this VIA must actually go thru. ;
ALRCLASS (ARRIVAL) (REXPR THRU; RPTR(DEXPR) ACTPLACE);
ALRCLASS (DEPARTURE) (REXPR THRU; RPTR(DEXPR) ACTPLACE);
ALRCLASS (VELOCITY) (REXPR VELOC);
ALRCLASS (F_FRAME) (REXPR FRAME; INTEGER C_SYS);
ALRCLASS (FORCE) (REXPR DIRECT, VAL; INTEGER TYPE,REL; RPTR(F_FRAME) F_F);
! Type: Torque = False
Force = True
Rel: < = SIGLT
≥ = SIGGE;
! The following bits are used during calls to the force sensing system;
DEFINE YELARM = '1; ! Yellow arm;
DEFINE BLUARM = '4; ! Blue arm;
DEFINE FTABLE = '400; ! Force trans (C) in table coordinates;
DEFINE FHAND = '0; ! " " " " hand coordinate system;
DEFINE XFORCE = '0; ! Force along X direction of C;
DEFINE YFORCE = '1000; ! " " Y " " ";
DEFINE ZFORCE = '2000; ! " " Z " " ";
DEFINE XMOMENT = '3000; ! Moment about X direction of C;
DEFINE YMOMENT = '4000; ! " " Y " " ";
DEFINE ZMOMENT = '5000; ! " " Z " " ";
DEFINE FSTOP = '10000; ! In addition to starting cmon, stop arm;
DEFINE SIGGE = '100000; ! Start cmon if force ≥ specified value;
DEFINE SIGLT = '0; ! " " " " < " ";
ALRCLASS (SETBASE) (REXPR VAL); ! These are temporary hacks for JKS;
ALRCLASS (WRIST) (REXPR VAL); ! so he can debug the froce wrist;
ALRCLASS (OPENING) (REXPR VAL);
ALRCLASS (WOBBLE) (REXPR VAL);
ALRCLASS (S_FAC) (REXPR VAL);
ALRCLASS (NNULL) (INTEGER FLAG);
! Assertions & compile-time conditionals;
ALRCLASS (NOMV)(REXPR E;ITEMVAR WLD);
ALRCLASS (BINDV)(RPTR(VARIABLE) VAR;RANY RESULT);
ALRCLASS (SFACT)(RCELL PATT;RPTR(FACT) FACT);
ALRCLASS (AFACT)(INTEGER RELN;REXPR LEFT,RIGHT);
ALRCLASS (ASSERT)(ITEMVAR WLD;RPTR(SFACT,AFACT) FACT);
ALRCLASS (DENY)(ITEMVAR WLD;RPTR(SFACT,AFACT) FACT);
ALRTYPE (PAS);
ALRCLASS (CIF) (REXPR COND; RSTMNT THN, ELS);
! Totally miscellaneous things;
ALRCLASS (IDENT)(RANY ITEMVAR ID); ! used by GOBBLE;
ALRCLASS (COMMNT) (RANY HESAYS);
ALRCLASS (NOTE) (RPTR(STCONST) HESAYS);
ALRCLASS (NOTE1) (RPTR(STCONST) HESAYS);
ALRCLASS (NOTE2) (RPTR(STCONST) HESAYS);
ALRECTERNAL RCLASS AFXDATA(RVAR A,B,T,YOUNGEST;
REXPR INVT;
RPTR(CALCULATOR) C1,C2;
RPTR(CHANGER) CHG);
! data types;
DEFINE DEFDTYPE(MNE,XXX) "<>" = <
REDEFINE NDTYPES=NDTYPES+1;
ASSIGNC XXX = CVPS(MNE)&"_DTYPE";
DEFINE XXX = NDTYPES>;
DEFINE NDTYPES=-1;
DEFDTYPE(INVALID); ! 0 is invalid;
DEFDTYPE(SVAL); ! scalar;
DEFDTYPE(V3ECT); ! 3 vector;
DEFDTYPE(ROTN); ! rotation;
DEFDTYPE(TRANS); ! transform matrix;
DEFDTYPE(FRAME); ! frame;
DEFDTYPE(ARAY); ! array;
DEFDTYPE(PROC); ! procedure;
DEFDTYPE(REF); ! call by referemce;
DEFDTYPE(VAL); ! call by value;
DEFDTYPE(ATOM); ! an "atom" -- symbolic only;
DEFDTYPE(STATEMENT); ! a "statement" variable;
DEFDTYPE(CLAUSE); ! a "clause" variable;
DEFDTYPE(EXPRESSION); ! an "expression" variable;
DEFDTYPE(EVENT); ! an "event" variable;
DEFDTYPE(WORLD); ! a "world" variable;
! note: Labels get loaded up with the semantics of the thing
pointed to.
;
DEFDTYPE(STMLAB); ! statement label;
DEFDTYPE(CLCLAB); ! calculator label;
DEFDTYPE(CHGLAB); ! changer label;
DEFDTYPE(OMNLAB); ! on-monitor label;
! operators for expressions;
IFCR ALRECSW THENC
DEFINE DEFOP(MNE,XXX,YYY) "<>" = <
REDEFINE NOPS=NOPS+1;
ASSIGNC XXX = CVPS(MNE)&"_OP";
ASSIGNC YYY = """"&CVPS(MNE)&"""";
YYY,
DEFINE XXX = NOPS>;
PRELOAD_WITH
ELSEC
DEFINE DEFOP(MNE,XXX) "<>" = <
REDEFINE NOPS=NOPS+1;
ASSIGNC XXX = CVPS(MNE)&"_OP";
DEFINE XXX = NOPS>;
ENDC
DEFINE OPBRK(MXID,MNID,XXX) "<>" = <
ASSIGNC XXX = "MAX_"&CVPS(MXID)&"_OP";
DEFINE XXX = NOPS;
ASSIGNC XXX = "MIN_"&CVPS(MNID)&"_OP";
DEFINE XXX = NOPS+1>;
! This list updated 6/30/75 by RF & 7/25/76, 3/2/78 & 5/28/78 by ARG;
DEFINE NOPS=-1; ! Return nothing;
DEFOP(INVALID);
DEFOP(AREF); ! array reference - returns ?;
DEFOP(CALL); ! procedure call - returns ?;
DEFOP(NO); ! no-op;
OPBRK(NO,SVAL); ! Return scalars;
DEFOP(SSBRTN); ! arithmetic functions: sqrt,sin,cos,asin,acos,atan2,log,exp;
DEFINE SQRT_OP = "1", SIN_OP = "2", COS_OP = "3", ASIN_OP = "4",
ACOS_OP = "5", ATAN2_OP = "6", LOG_OP = "7", EXP_OP = "8";
DEFOP(SCALRD); ! reads in scalar from console;
DEFOP(QUERY); ! reads in boolean from console;
DEFOP(SABS); ! |s| (absolute value);
DEFOP(SADD); ! s+s;
DEFOP(SSUB); ! s-s;
DEFOP(SNEG); ! -s;
DEFOP(SMUL); ! s*s;
DEFOP(SDIV); ! s/s;
DEFOP(SEXP); ! s↑s (not in runtime);
DEFOP(MAX); ! s MAX s;
DEFOP(MIN); ! s MIN s;
DEFOP(INT); ! INT s;
DEFOP(DIV); ! INT(s/s);
DEFOP(MOD); ! s MOD s;
DEFOP(SLT); ! S<S;
DEFOP(SEQ); ! S=S;
DEFOP(SLE); ! S≤S;
DEFOP(SGE); ! S≥S;
DEFOP(SNE); ! S≠S;
DEFOP(SGT); ! S>S;
DEFOP(AND); ! S∧S;
DEFOP(OR); ! S∨S;
DEFOP(XOR); ! S⊗S;
DEFOP(EQV); ! S≡S;
DEFOP(NOT); ! ¬S;
DEFOP(VMAGN); ! |v|;
DEFOP(VDOT); ! v.v;
DEFOP(RMAGN); ! rotation angle of r;
OPBRK(SVAL,V3ECT); ! Return vectors;
DEFOP(VMAKE); ! vector(s,s,s);
DEFOP(SVMUL); ! s*v;
DEFOP(VSDIV); ! v/s;
DEFOP(VADD); ! v+v;
DEFOP(VSUB); ! v-v;
DEFOP(VCROSS); ! v*v;
DEFOP(RVMUL); ! r*v;
DEFOP(TVMUL); ! t*v;
DEFOP(UVECT); ! v/magn(v);
DEFOP(AXIS); ! rotation axis of r;
DEFOP(POS); ! position part of a trans;
OPBRK(V3ECT,ROTN); ! Return rotns;
DEFOP(AXW_ROTN); ! rotation of s radians about v;
DEFOP(ORIENT); ! rotation part of t;
DEFOP(RRMUL); ! r*r;
OPBRK(ROTN,TRANS); ! Return transes;
DEFOP(TMAKE); ! trans(r,v);
DEFOP(CONSTR); ! constr(v,v,v) (origin,x-axis,xy-plane);
DEFOP(FTOF); ! f → f;
DEFOP(TVADD); ! t+v;
DEFOP(TVSUB); ! t-v;
DEFOP(TTMUL); ! t*t;
DEFOP(TINVRT); ! inverse(t);
DEFOP(DEPR); ! deproach(f);
OPBRK(TRANS,FRAME); ! Return frames;
DEFOP(FMAKE); ! frame(R,v);
DEFOP(TFMAKE); ! frame(T);
OPBRK(FRAME,LAST);
DEFOP(LAST); ! Never return;
IFCR ALRECSW THENC
"INVALID";INTERNAL STRING ARRAY OP_MNE[0:LAST_OP+1];
ELSEC
EXTERNAL STRING ARRAY OP_MNE[0:LAST_OP+1];
ENDC
! predeclared variables & internal system variables;
IFCR ALRECSW THENC
DEFINE PDVSW = [TRUE]; ! RF. I couldn't find global BARM, etc.;
DEFINE ALITM(ID,SID,NARG,INID) "<>" =
<IFCR LENGTH(CVPS(SID)) THENC
DEFINE ID = CVPS(SID);
ENDC
INTERNAL ITEMVAR ID;
ASSIGNC INID = "INI_"&CVPS(ID);
SIMPLE PROCEDURE INID;
BEGIN
ID←CVSI(CVPS(ID),ALIFG);
IF ALIFG THEN
NEW_PNAME(ID←NEW NARG,CVPS(ID));
END;
REQUIRE INID INITIALIZATION>;
INTEGER ALIFG;
ELSEC
DEFINE ALITM(ID,SID,NARG,INID) "<>" =
<IFCR LENGTH(CVPS(SID)) THENC
DEFINE ID = CVPS(SID);
ENDC
EXTERNAL ITEMVAR ID>;
ENDC
IFCR ¬DECLARATION(PDVSW) THENC DEFINE PDVSW = [FALSE]; ENDC
IFCR PDVSW THENC
DEFINE PDV(ID,HDT,OFFST,INID) "<>" =
<
INTERNAL RPTR(VARIABLE) ID;
IFCR ¬LENGTH(CVPS(INID)) THENC ASSIGNC INID="INI_"&CVPS(ID); ENDC
PROCEDURE INID;
BEGIN
RANY ITEMVAR IV;
EXTERNAL RPTR(VARIABLE) PROCEDURE NEW_VAR(ITEMVAR IV;INTEGER DT;
RPTR(BLOCK) BID);
IV←NEW(NULL_RECORD);
NEW_PNAME(IV,"ID");
∂(IV)←ID←NEW_VAR(IV,HDT,NULL_RECORD);
VARIABLE:OFFSET[ID] ← OFFST;
END;
REQUIRE INID INITIALIZATION>;
ELSEC
DEFINE PDV(ID,HDT,OFFST) "<>" =
< EXTERNAL RPTR(VARIABLE) ID >;
ENDC
PDV(YARM,FRAME_DTYPE,'0); ! was 10;
PDV(YHAND,SVAL_DTYPE,'1); ! was 12;
PDV(BARM,FRAME_DTYPE,'2); ! was 14;
PDV(BHAND,SVAL_DTYPE,'3); ! was 16;
PDV(NEWV,FRAME_DTYPE,'0); ! Added by RF. Never use the offset;
PDV(OLDV,FRAME_DTYPE,'0); ! Added by RF. Never use the offset;
PDV(YDEPROACH,FRAME_DTYPE,'4); ! added by arg was 32;
PDV(BDEPROACH,FRAME_DTYPE,'5); ! added by arg was 30;
PDV(SPEED_FACTR,SVAL_DTYPE,0); ! added by arg. Never use the offset;
! some standard pattern things;
DEFINE AFFIXED "<>" = <ATCHD>;
PDV(AFFIXED,ATOM_DTYPE,0);
PDV(WAS_AFFIXED,ATOM_DTYPE,0);
PDV(RIGIDLY,ATOM_DTYPE,0);
PDV(NONRIGIDLY,ATOM_DTYPE,0);
PDV(DEPROACH,ATOM_DTYPE,0);
! Patterns:
[ AFFIXED,f1,f2,byvar,rgf ]
rgf = RIGIDLY or NONRIGIDLY
;
REQUIRE UNSTACK_DELIMITERS;
ALRECEND